package simtester.cloud

import static org.junit.Assert.*

import com.amazonaws.auth.PropertiesCredentials
import com.amazonaws.services.ec2.AmazonEC2Client
import groovy.mock.interceptor.MockFor
import groovy.mock.interceptor.StubFor
import net.schmizz.sshj.SSHClient
import java.util.logging.Logger
import java.util.logging.Level
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.junit.After
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test

class CloudInstanceTests {
    static testFilename = 'testfile.txt'
    static testFile = new File(testFilename)
    static testAddress = '111.222.333.444'
    static testUser = 'ec2-user'
    static testCredentials
    static testEndpoint
    static testAuthkey
    
    def testInstanceIds
    
    @BeforeClass
    static void setUpClass() {
        //BasicConfigurator.configure()
        Logger.getLogger("").setLevel(Level.SEVERE)
        
        def config = new ConfigSlurper().parse(new File('conf/SimTesterConfig.groovy').toURI().toURL())
        testCredentials = config.aws.credentialsfile
        testEndpoint    = config.aws.endpoint
        testAuthkey     = config.aws.key
    }
    
    @Before
    void setUp() {
        testInstanceIds = []
    }
    
    @After
    void tearDown() {
        def cloud = new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey)
        cloud.removeInstances(testInstanceIds)
    }

    @Test
    void sendFile() {
        // SETUP
        def uploadArgs = [:]
        def mockCloudInstanceSSH = new MockFor(CloudInstanceSSH)
        mockCloudInstanceSSH.demand.with {
            upload { opts, f ->
                uploadArgs.opts = opts
                uploadArgs.file = f
            }
        }
        
        // DO TEST
        def testFileDest = '/home/ec2-user'
        mockCloudInstanceSSH.use {
            def instance = new CloudInstance(cloud: new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey),
                                             id: 'ami-1bd68a5e',
                                             publicDnsName: 'ec2-some-dns-name',
                                             publicIpAddress: testAddress,
                                             state: 'running')

            instance.sendFile(testFile, to: testFileDest)
        }
        
        // VERIFY
        assert uploadArgs.opts.to   == testFileDest
        assert uploadArgs.file.name == testFilename
    }
    
    @Test
    void checkFileExistence() {
        // SETUP
        def testFileCheck = "/home/ec2-user/$testFilename"
        def execString
        def mockCloudInstanceSSH = new MockFor(CloudInstanceSSH)
        mockCloudInstanceSSH.demand.with {
            exec { cmd ->
                execString = cmd
                testFileCheck
            }
            exec { cmd ->
                'ls: cannot access $testFileCheck: No such file or directory'
            }
            exec { cmd ->
                ''
            }
        }

        // DO TEST
        boolean fileExists1
        boolean fileExists2
        boolean fileExists3
        mockCloudInstanceSSH.use {
            def instance = new CloudInstance(cloud: new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey),
                                             id: 'ami-1bd68a5e',
                                             publicDnsName: 'ec2-some-dns-name',
                                             publicIpAddress: testAddress,
                                             state: 'running')

            fileExists1 = instance.fileExists(testFileCheck)
            fileExists2 = instance.fileExists(testFileCheck)
            fileExists3 = instance.fileExists(testFileCheck)
        }
        
        // VERIFY
        assert fileExists1 == true
        assert fileExists2 == false
        assert fileExists3 == false
        assert execString  == "ls $testFileCheck"
    }
    
    @Test
    void viewFileText() {
        // SETUP
        def testText = 'test file text'
        def execString
        def mockCloudInstanceSSH = new MockFor(CloudInstanceSSH)
        mockCloudInstanceSSH.demand.with {
            exec { cmd ->
                execString = cmd
                testText
            }
        }
        
        // DO TEST
        def text
        def instanceFile = '/home/ec2-user/somefile.txt'
        mockCloudInstanceSSH.use {
            def instance = new CloudInstance(cloud: new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey),
                                             id: 'ami-1bd68a5e',
                                             publicDnsName: 'ec2-some-dns-name',
                                             publicIpAddress: testAddress,
                                             state: 'running')
            text = instance.viewText(instanceFile)
        }
        
        // VERIFY
        assert execString == "cat $instanceFile"
        assert text       == testText
    }
    
    @Test
    void execCommand() {
        // SETUP
        def cannedOutput = 'stuff'
        def execCmds = []
        def mockCloudInstanceSSH = new MockFor(CloudInstanceSSH)
        mockCloudInstanceSSH.demand.with {
            exec { cmd ->
                execCmds << cmd
                cannedOutput
            }
            execAsync { cmd ->
                execCmds << cmd
            }
        }
        
        // DO TEST
        def cannedCmds = ['ls /home/ec2-user', 'some cmd']
        def actualOutput
        mockCloudInstanceSSH.use {
            def instance = new CloudInstance(cloud: new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey),
                                             id: 'ami-1bd68a5e',
                                             publicDnsName: 'ec2-some-dns-name',
                                             publicIpAddress: testAddress,
                                             state: 'running')
            actualOutput = instance.exec(cannedCmds[0])
            instance.execAsync(cannedCmds[1])
        }
        
        // VERIFY
        assert actualOutput == cannedOutput
        assert execCmds     == cannedCmds
    }
    
    @Test
    void cannotFindInstanceIp() {
        def stubCloudInstance = new StubFor(CloudInstance)
        stubCloudInstance.ignore.with {
            updateInfo { ->  }
        }
        
        def instance = new CloudInstance(cloud: new Cloud(creds: testCredentials, endpoint: testEndpoint, key: testAuthkey),
                                         id: 'ami-1bd68a5e',
                                         state: 'running',
                                         test: true)
        
        stubCloudInstance.use {
            try {
                instance.fileExists('hello')
                fail 'CloudException not thrown'
            } catch (CloudException e) {
                assert e.message == 'Instance not ready to be used after waiting for 4 minutes'
            }
        }
    }

}
